// ws.dart
import 'dart:async';
import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:multi_shop/models/msg.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

typedef MsgHandler = void Function(MallChatMessage msg);
typedef ErrorHandler = void Function(Object error);

final chatWs = ChatWs();

class ChatWs {
  WebSocketChannel? _ch;
  StreamSubscription? _sub;
  Timer? _pingTimer;
  Timer? _reconnectTimer;

  final _msgCtrl = StreamController<MallChatMessage>.broadcast();
  Stream<MallChatMessage> get messages => _msgCtrl.stream;

  bool get connected => _ch != null;

  Uri? _lastUri;
  int _retry = 0;

  void connect({required Uri uri, Map<String, String>? params}) {
    _lastUri = uri;
    _doConnect(uri, params: params);
  }

  void _doConnect(Uri uri, {Map<String, String>? params}) {
    close(); // 清理旧连接
    _retry = 0;
    _ch = WebSocketChannel.connect(uri);
    _sub = _ch!.stream.listen(
      (data) {
        try {
          final Map<String, dynamic> m = data is String
              ? json.decode(data)
              : Map<String, dynamic>.from(data);

          MallChatMessage msg = MallChatMessage.fromJson(m);
          debugPrint(json.encode(msg));
          _msgCtrl.add(msg);
        } catch (_) {
          // 忽略格式异常
        }
      },
      onError: (e) {
        debugPrint("ws error!");
        _scheduleReconnect();
      },
      onDone: () {
        debugPrint("ws done!");
        // _cleanTimers();
      },
    );

    // 心跳（后端若不需要可去掉）
    _pingTimer = Timer.periodic(const Duration(seconds: 20), (_) {
      try {
        _ch?.sink.add(
          json.encode({
            "type": "ping",
            "ts": DateTime.now().millisecondsSinceEpoch,
          }),
        );
      } catch (_) {}
    });
  }

  /// 发送文本消息（只支持文本）
  void sendText(MallChatMessage msg) {
    if (_ch == null) return;
    _ch!.sink.add(json.encode(msg.toJson()));
  }

  void _scheduleReconnect() {
    _cleanTimers();
    _ch = null;
    _sub?.cancel();
    _sub = null;

    if (_lastUri == null) return;

    // 指数退避，最大 10s
    _retry = (_retry + 1).clamp(1, 5);
    final delay = Duration(seconds: 2 * _retry);
    _reconnectTimer = Timer(delay, () {
      _doConnect(_lastUri!);
    });
  }

  void _cleanTimers() {
    _pingTimer?.cancel();
    _reconnectTimer?.cancel();
    _pingTimer = null;
    _reconnectTimer = null;
  }

  void close() {
    _cleanTimers();
    _sub?.cancel();
    _sub = null;
    _ch?.sink.close();
    _ch = null;
  }

  void dispose() {
    close();
    _msgCtrl.close();
  }
}
